package cn.zk.commom.tcp.obd;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;

//@Component
//@Scope("prototype")
public class ObdAnalyse {
    private ByteArrayOutputStream mLastData;    //上次残留数据帧
    private byte[] mCurrData;   //当前数据

    //构造函数
    public ObdAnalyse(){
        mLastData = new ByteArrayOutputStream();
        mCurrData = new byte[1024];
    }

    public   int        mMsgId;                  //消息ID
    public   String     mPhoneNumber;           //手机号
    public   int        mMsgSerialNumber;       //消息流水号
    public  ArrayList<Position> mPosition;      //位置信息

    //
    public void ObdInfo(byte[] data){
        mMsgId = (int)ByteArrayToLong(data, 1 ,2);
        int len = (int)ByteArrayToLong(data, 3 ,2);

        //手机号
        StringBuilder str1 = new StringBuilder();
        if (data.length>11){
            for (int i = 5; i < 11; ++i) {
                str1.append(String.format("%02x", data[i]));
            }
        }
        mPhoneNumber = str1.toString();
        mMsgSerialNumber = (int)ByteArrayToLong(data, 11, 2);

        //解析位置信息
        ByteArrayOutputStream _tep = new ByteArrayOutputStream();

        if (data.length>13){
            _tep.write(data, 13, len);
        }
        ArrayList<Position> _positionArray = new ArrayList<>();
        if(mMsgId == 0x0200) {
            mPosition = AnalyseByteData_0200(_tep.toByteArray());
        } else if(mMsgId == 0x0704)  {
            mPosition = AnalyseByteData_0704(_tep.toByteArray());
        } else if(mMsgId == 0x0900) {
            mPosition = AnalyseByteData_0704(_tep.toByteArray());
        } else {
            mPosition = new ArrayList<>();
        }
    }

    /***
     * 解析 msgID = 0x0200
     * @param data 完整的数据帧，包含头尾0x7e
     * @return 返回位置信息
     */
    private ArrayList<Position> AnalyseByteData_0200(byte[] data) {
        ArrayList<Position> ret = new ArrayList<>();
        Position _pos = new Position(data);
        if(_pos.mIsValid) {
            ret.add(_pos);
        }
        return ret;
    }

    /***
     * 解析 msgID = 0x0704
     * @param data 完整的数据帧，包含头尾0x7e
     * @return 返回位置信息
     */
    private ArrayList<Position> AnalyseByteData_0704(byte[] data) {
        ArrayList<Position> ret = new ArrayList<>();
        ArrayList<ByteArrayOutputStream>  _addMsg = AdditionalMsgDataSplitting(data);
        for(ByteArrayOutputStream var : _addMsg)
        {
            Position _pos = new Position(var.toByteArray());
            if(_pos.mIsValid) {
                ret.add(_pos);
            }
        }
        return ret;
    }

    /***
     * 解析 msgID = 0x0900
     * @param data 完整的数据帧，包含头尾0x7e
     * @return 返回位置信息
     */
    private ArrayList<Position> AnalyseByteData_0900(byte[] data) {
        ArrayList<Position> ret = new ArrayList<>();
//        ArrayList<ByteArrayOutputStream>  _addMsg = AdditionalMsgDataSplitting(data);
//        for(ByteArrayOutputStream var : _addMsg)
//        {
//            Position _pos = new Position(var.toByteArray());
//            if(_pos.mIsValid) {
//                ret.add(_pos);
//            }
//        }
        return ret;
    }

    /***
     * 拆分批量汇报数据
     * @param data 批量汇报数据
     * @return
     */
    private ArrayList<ByteArrayOutputStream> AdditionalMsgDataSplitting(byte[] data) {
        ArrayList<ByteArrayOutputStream> ret = new ArrayList<>();

        int sub_index = 3;
        while(true)
        {
            if(sub_index + 2 > data.length )
                break;

            //0:子类型; 1:子类型长度
            int sub_len  = (int)ByteArrayToLong(data, sub_index, 2);   //子长度
            int new_index = sub_index + sub_len + 2;

            if(new_index > data.length)
                break;

            ByteArrayOutputStream _tep = new ByteArrayOutputStream();
            _tep.write(data, sub_index + 2, sub_len);
            ret.add(_tep);

            sub_index = new_index;
        }

        return  ret;
    }

    /***
     * 解析从网络接收到的数据
     * @param data 来自网络的数据
     */
    public byte[] AnalyseTcpData(byte[] data) {
        //校验传入的数据是否合法，如果合法则直接解析
        //如果不合法则加入到上次一流数据中进行解析
        byte[] ret = RecvFrameDataCheck(data);
        if(null != ret){
            mLastData.reset();
            mCurrData = ret;
            return ret;
        }

        //传入数据不合法，追加数据
        mLastData.write(data, 0, data.length);

        //查找所有的0x7e
        byte[] _tep1 = mLastData.toByteArray();
        ArrayList<Integer> index7e = new ArrayList<>();
        for(int i = 0; i < _tep1.length; ++i)
        {
            if(_tep1[i] == (byte)0x7e)
                index7e.add(i);
        }

        //验证0x7e合法性
        //ArrayList<ByteArrayOutputStream> validDataFrame = new ArrayList<ByteArrayOutputStream>();
        ByteArrayOutputStream _tep2 = new ByteArrayOutputStream();
        ArrayList<byte[]> validDataFrame = new ArrayList<>();
        while (true)
        {
            if(index7e.size() < 2)
                break;

            int len = index7e.get(1) - index7e.get(0) + 1;

            //头尾间距不符合,抛弃第一个，重新找
            if( len < 15) {
                index7e.remove(0);
                continue;
            }

            //7e 、长度 都符合了，开始 替换校验
            _tep2.reset();
            _tep2.write(_tep1, index7e.get(0), len);
            ret = RecvFrameDataCheck(_tep2.toByteArray());
            if(null == ret){
                index7e.remove(0);  //7e 和长度组合不符合，非法7e，,抛弃第一个，重新找
                continue;
            }

            //所有条件都符合了，开始拷贝
            validDataFrame.add(ret);

            //
            index7e.remove(1);
            index7e.remove(0);
        }

        mCurrData = null;
        if(index7e.size() == 0){
            mLastData.reset();
        } else { //index7e.size() == 1
            mLastData.reset();
            mLastData.write(_tep1, index7e.get(0), _tep1.length - index7e.get(0));
        }

        if(validDataFrame.size() > 0) {
            mCurrData = validDataFrame.get(validDataFrame.size() - 1);
        }

        return mCurrData;
    }


    /***
     * 打印数据
     * 如果有合法数据则打印，没有则不打印
     */
    public StringBuilder PrintFrameData() {
        if(null == mCurrData)
            return null;

        StringBuilder str = new StringBuilder();
        for(byte var : mCurrData)
        {
            str.append(String.format("%02x", var));
        }

        System.out.println("解析后：size=" + mCurrData.length + " " + str);
        return str;
    }

    /***
     * 生成通用指令响应数据
     * 如果是注册消息或者没有收到合法的消息则返回null，
     * 否则返回通用响应指令
     */
    public byte[] GenerateCommonMsgSendData() {
        if (null == mCurrData)
            return null;
        //注册消息应答
        if (mCurrData[1] == (byte) 0x01 && mCurrData[2] == (byte) 0x00)
            return null;
        //通用应答
        ByteArrayOutputStream ret = new ByteArrayOutputStream();
        ret.write(0x80);
        ret.write(0x01);
        ret.write(0x00);//长度
        ret.write(0x05);
        ret.write(mCurrData[5]); //总端Id
        ret.write(mCurrData[6]);
        ret.write(mCurrData[7]);
        ret.write(mCurrData[8]);
        ret.write(mCurrData[9]);
        ret.write(mCurrData[10]);
        ret.write(0x00); //消息流水号
        ret.write(0x00);
        ret.write(mCurrData[11]); //应答消息流水号
        ret.write(mCurrData[12]);
        ret.write(mCurrData[1]); //应答消息ID
        ret.write(mCurrData[2]);
        ret.write(0x00);  //应答结果
        ret.write(SendFrameDataCheck(ret.toByteArray())); //校验码
        return SendFrameDataReplace(ret.toByteArray());
    }

    /***
     * 生成注册消息响应数据
     * 如果不是注册消息则返回null
     * 否则返回注册消息的响应数据
     */
    public byte[] GenerateRegisterMsgSendData() {
        if (null == mCurrData) {
            return null;
        }
        //注册消息应答
        if (mCurrData[1] != (byte) 0x01 || mCurrData[2] != (byte) 0x00)
            return null;
        //注册消息
        ByteArrayOutputStream ret = new ByteArrayOutputStream();
        ret.write(0x81);//注册应答
        ret.write(0x00);
        ret.write(0x00);//长度
        ret.write(0x13);
        ret.write(mCurrData[5]); //总端Id
        ret.write(mCurrData[6]);
        ret.write(mCurrData[7]);
        ret.write(mCurrData[8]);
        ret.write(mCurrData[9]);
        ret.write(mCurrData[10]);
        ret.write(0x00); //消息流水号
        ret.write(0x00);
        ret.write(mCurrData[11]); //应答消息流水号
        ret.write(mCurrData[12]);
        ret.write(0x00);  //应答结果

        //写入鉴权码:YunJianXinChuang
        byte[] _key = {0x59, 0x75, 0x6E, 0x4A, 0x69, 0x61, 0x6E, 0x58, 0x69, 0x6E, 0x43, 0x68, 0x75, 0x61, 0x6E, 0x67};
        ret.write(_key, 0, _key.length);  //鉴权码

        ret.write(SendFrameDataCheck(ret.toByteArray())); //校验码
        return SendFrameDataReplace(ret.toByteArray());
    }

    /***
     * 判断当前数据帧是否是鉴权消息，
     * 如果是鉴权消息则返回true
     * 如果不是鉴权消息或者鉴权消息的鉴权码不合法则返回false
     */
    public boolean GenerateAuthenticationMsgSendData() {
        if (null == mCurrData)
            return false;

        //鉴权消息应答
        if (mCurrData[1] != (byte) 0x01 || mCurrData[2] != (byte) 0x02)
            return false;

        //验证鉴权码:YunJianXinChuang
        byte[] _key = {0x59, 0x75, 0x6E, 0x4A, 0x69, 0x61, 0x6E, 0x58, 0x69, 0x6E, 0x43, 0x68, 0x75, 0x61, 0x6E, 0x67};
        for (int i = 0; i < 16; ++i) {
            if (mCurrData[13 + i] != _key[i])
                return false;
        }

        return true;
    }

    /**
     * 判断传入的数组data是否是合法的数据帧
     * @param data 待校验的数据
     * @return 如果不合法则返回null，合法则返回替换转义后得数组
     */
    private byte[] RecvFrameDataCheck(byte[] data) {
        //头尾7e校验
        if(data.length < 15 || data[0] != (byte) 0x7e || data[data.length - 1] != (byte) 0x7e)
            return null;

        //转义数据
        ByteArrayOutputStream  tep = RecvFrameDataReplace(data);

        //校验长度
        int msgLen = (int)ByteArrayToLong(tep.toByteArray(), 3 , 2);
        if(msgLen + 15 != tep.size())
            return null;

        //校验码
        if( RecvFrameDataCheckSum(tep.toByteArray()))
            return tep.toByteArray();

        return null;
    }

    /***
     * 接收数据，转义数据
     * 0x7d 0x01 替换成 0x7d；
     * 0x7d 0x02 替换成 0x7e
     * @param data 等待处理的数据，,包含头尾的0x7e
     * @return 返回处理后的数据长度
     */
    private ByteArrayOutputStream  RecvFrameDataReplace(byte[] data) {
        ByteArrayOutputStream _r = new ByteArrayOutputStream();
        for(int i = 0; i < data.length - 1;)
        {
            if(data[i] == 0x7d && data[i + 1] == 0x01) {
                _r.write(0x7d);
                i += 2;
            } else if(data[i] == 0x7d && data[i + 1] == 0x02) {
                _r.write(0x7e);
                i += 2;
            } else {
                _r.write(data[i]);
                i += 1;
            }
        }

        _r.write(0x7e);
        return _r;
    }

    /***
     * 接收数据，校验数据
     * @param data 等待校验的数据，需要包含头尾的0x7e
     * @return 合法返回true，非法返回false
     */
    private Boolean RecvFrameDataCheckSum(byte[] data) {
        byte ret = data[1];
        for(int i = 2; i < data.length - 2; ++i)
        {
            ret  = (byte) (ret ^ data[i]);
        }
        return data[data.length - 2] == ret;
    }

    /***
     * 发送数据，转义数据
     * @param data 待转义的数据,不包含头尾的0x7e
     * @return 返回替换后的数据
     */
    private byte[] SendFrameDataReplace(byte[] data) {
        ByteArrayOutputStream _r = new ByteArrayOutputStream();
        _r.write(0x7e);
        for(byte var : data)
        {
            if(var == (byte)0x7e) {
                _r.write(0x7d);
                _r.write(0x02);
            } else if(var == (byte)0x7d) {
                _r.write(0x7d);
                _r.write(0x01);
            } else {
                _r.write(var);
            }
        }
        _r.write(0x7e);

        return  _r.toByteArray();
    }

    /***
     * 发送数据生成校验码
     * @param data 等待校验的数据,不包含头0x7e
     * @return 返回生成的校验码
     */
    private byte SendFrameDataCheck(byte[] data) {
        byte ret = data[0];
        for(int i = 1; i < data.length; ++i)
        {
            ret  = (byte) (ret ^ data[i]);
        }

        return  ret;
    }

    /***
     * byte[]转换成long
     * @param data 用来转换的Byte数组
     * @param index：转换的起始索引
     * @param len：转换的长度
     * @return 返回解析后的值
     */
    public static long ByteArrayToLong(byte[] data, int index ,int len) {
        if(index < 0 || len <= 0 || index >= data.length || len + index > data.length)
            return  0;

        //
        long ret = (data[index] & 0xff);
        for(int i = 1; i < len; ++i)
        {
            ret = (ret << 8)  + (data[i + index] & 0xff);
        }

        return  ret;
    }

    @Override
    public String toString() {
        return "ObdAnalyse{" +
                "mLastData=" + mLastData +
                ", mCurrData=" + Arrays.toString(mCurrData) +
                ", mMsgId=" + mMsgId +
                ", mPhoneNumber='" + mPhoneNumber + '\'' +
                ", mMsgSerialNumber=" + mMsgSerialNumber +
                ", mPosition=" + mPosition +
                '}';
    }
}




